using System;
using System.IO;
using System.Security;
using System.Security.Cryptography;
using System.Runtime.Serialization;
using System.Xml.Serialization;

namespace Pricom
{
  public class Crypto
  {
    public byte[] Encrypt(string rawText)
    {
      byte[] result = null;
      try
      {
        System.Text.UnicodeEncoding encoder = new System.Text.UnicodeEncoding();
        result = encoder.GetBytes(rawText);
        result = rsa.Encrypt(result, false);
        //encoder.GetString(b);
      }
      catch
      {
      }
      return result;
    }
    public string Decrypt(byte[] encodedText, string password)
    {
      string result = "";
      try
      {
        // decrypt private keys
        RSAParameters rsaParams = new RSAParameters();
        rsaParams.Exponent = this.rsaParams.Exponent;
        rsaParams.Modulus = this.rsaParams.Modulus;
        rsaParams.P = SymmetricDecrypt(this.rsaParams.P, password);
        rsaParams.Q = SymmetricDecrypt(this.rsaParams.Q, password);
        rsaParams.DP = SymmetricDecrypt(this.rsaParams.DP, password);
        rsaParams.DQ = SymmetricDecrypt(this.rsaParams.DQ, password);
        rsaParams.InverseQ = SymmetricDecrypt(this.rsaParams.InverseQ, password);
        rsaParams.D = SymmetricDecrypt(this.rsaParams.D, password);

        // decrypt text
        byte[] b = rsa.Decrypt(encodedText, false);
        System.Text.UnicodeEncoding encoder = new System.Text.UnicodeEncoding();
        result = encoder.GetString(b);
      }
      catch
      {
      }
      return result;
    }
    public void SetKeys(RSAParameters rsaParams, string password)
    {
      this.rsaParams = rsaParams;

      // decrypt private keys
      rsaParams.P = SymmetricDecrypt(rsaParams.P, password);
      rsaParams.Q = SymmetricDecrypt(rsaParams.Q, password);
      rsaParams.DP = SymmetricDecrypt(rsaParams.DP, password);
      rsaParams.DQ = SymmetricDecrypt(rsaParams.DQ, password);
      rsaParams.InverseQ = SymmetricDecrypt(rsaParams.InverseQ, password);
      rsaParams.D = SymmetricDecrypt(rsaParams.D, password);

      rsa.ImportParameters(rsaParams);
    }
    public RSAParameters GenerateKeys(string password)
    {
      // get keys + encode
      RSAParameters rsaParams = rsa.ExportParameters(true);
      rsaParams.P = SymmetricEncrypt(rsaParams.P, password);
      rsaParams.Q = SymmetricEncrypt(rsaParams.Q, password);
      rsaParams.DP = SymmetricEncrypt(rsaParams.DP, password);
      rsaParams.DQ = SymmetricEncrypt(rsaParams.DQ, password);
      rsaParams.InverseQ = SymmetricEncrypt(rsaParams.InverseQ, password);
      rsaParams.D = SymmetricEncrypt(rsaParams.D, password);
      this.rsaParams = rsaParams;
      return rsaParams;
    }
    private void SaveKeys(string file)
    {
      // save to xml
      XmlSerializer xml = new XmlSerializer(typeof(RSAParameters));
      Stream stream = File.Open(file, FileMode.Create);
      xml.Serialize(stream, rsaParams);
      stream.Close();
    }
    private void LoadKeys(string file)
    {
      if (System.IO.File.Exists(file))
      {
        // load xml
        XmlSerializer xml = new XmlSerializer(typeof(RSAParameters));
        Stream stream = File.Open(file, FileMode.Open);
        rsaParams = (RSAParameters)xml.Deserialize(stream);
        stream.Close();

        // set public keys
        RSAParameters rsaParamsPublic = new RSAParameters();
        rsaParamsPublic.Exponent = rsaParams.Exponent;
        rsaParamsPublic.Modulus = rsaParams.Modulus;
        rsa.ImportParameters(rsaParamsPublic);
      }
    }

    public static byte[] SymmetricEncrypt(byte[] clearData, string Password)
    {
      byte[] salt = new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 };
      PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password, salt);
      byte[] Key = pdb.GetBytes(32);
      byte[] IV = pdb.GetBytes(16);

      MemoryStream ms = new MemoryStream();
      Rijndael alg = Rijndael.Create();
      alg.Key = Key;
      alg.IV = IV;
      CryptoStream cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write);
      cs.Write(clearData, 0, clearData.Length);
      cs.Close();
      byte[] encryptedData = ms.ToArray();
      return encryptedData;
    }
    public static byte[] SymmetricDecrypt(byte[] cipherData, string Password)
    {
      byte[] salt = new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 };
      PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password, salt);
      byte[] Key = pdb.GetBytes(32);
      byte[] IV = pdb.GetBytes(16);

      MemoryStream ms = new MemoryStream();
      Rijndael alg = Rijndael.Create();
      alg.Key = Key;
      alg.IV = IV;
      CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
      cs.Write(cipherData, 0, cipherData.Length);
      cs.Close();
      byte[] decryptedData = ms.ToArray();
      return decryptedData;
    }

    private RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
    private RSAParameters rsaParams = new RSAParameters();
  }
}